home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / cpp_libs / nihcl-30.lha / nihcl-3.0 / lib / Date.c < prev    next >
C/C++ Source or Header  |  1990-05-19  |  11KB  |  456 lines

  1. /* Date.c -- implementation of Gregorian calendar dates
  2.  
  3.     THIS SOFTWARE FITS THE DESCRIPTION IN THE U.S. COPYRIGHT ACT OF A
  4.     "UNITED STATES GOVERNMENT WORK".  IT WAS WRITTEN AS A PART OF THE
  5.     AUTHOR'S OFFICIAL DUTIES AS A GOVERNMENT EMPLOYEE.  THIS MEANS IT
  6.     CANNOT BE COPYRIGHTED.  THIS SOFTWARE IS FREELY AVAILABLE TO THE
  7.     PUBLIC FOR USE WITHOUT A COPYRIGHT NOTICE, AND THERE ARE NO
  8.     RESTRICTIONS ON ITS USE, NOW OR SUBSEQUENTLY.
  9.  
  10. Author:
  11.     Edward M. Persky
  12.     Bg. 12A, Rm. 2031
  13.     Computer Systems Laboratory
  14.     Division of Computer Research and Technology
  15.     National Institutes of Health
  16.     Bethesda, Maryland 20892
  17.     Phone: (301) 496-2963
  18.     uucp: uunet!nih-csl!tpersky
  19.     Internet: tpersky@alw.nih.gov
  20.  
  21. Function:
  22.     Provides an object that contains a date, stored as a Julian Day Number.
  23.     Note: Julian Day Number for Jan. 29, 1988 is not 88029; it is different.
  24.     
  25. $Log:    Date.c,v $
  26.  * Revision 3.0  90/05/20  00:19:23  kgorlen
  27.  * Release for 1st edition.
  28.  * 
  29. */
  30.  
  31. #include "Date.h"
  32. #include "String.h"
  33. #include <iomanip.h>
  34. #include <ctype.h>
  35. #include <string.h>
  36. #include <time.h>
  37. #include "nihclIO.h"
  38.  
  39. #define    THIS    Date
  40. #define    BASE    Object
  41. #define BASE_CLASSES BASE::desc()
  42. #define MEMBER_CLASSES
  43. #define VIRTUAL_BASE_CLASSES Object::desc()
  44. #define PARTIAL 0
  45. #define FULL    1
  46.  
  47. DEFINE_CLASS(Date,2,"$Header: /afs/alw.nih.gov/unix/sun4_40c/usr/local/src/nihcl-3.0/share/lib/RCS/Date.c,v 3.0 90/05/20 00:19:23 kgorlen Rel $",NULL,NULL);
  48.  
  49. extern const int NIHCL_BADMODAY,NIHCL_BADDAYNAM,NIHCL_BADMONAM,NIHCL_BADMONTH,NIHCL_BADDAY,
  50.     NIHCL_RDFAIL;
  51.  
  52. static const unsigned char days_in_month[12] = {31,28,31,30,31,30,31,31,30,31,30,31 };
  53. static const dayTy first_day_of_month[12] = {1,32,60,91,121,152,182,213,244,274,305,335 };
  54. static const char* month_names[12] = {"January","February","March","April","May","June",
  55.     "July","August","September","October","November","December" };
  56. static const char* uc_month_names[12] = {"JANUARY","FEBRUARY","MARCH","APRIL","MAY","JUNE",
  57.     "JULY","AUGUST","SEPTEMBER","OCTOBER","NOVEMBER","DECEMBER" };
  58. static const char* week_day_names[7] = {"Monday","Tuesday","Wednesday",
  59.     "Thursday","Friday","Saturday","Sunday" };
  60. static const char* uc_week_day_names[7] = {"MONDAY","TUESDAY","WEDNESDAY",
  61.     "THURSDAY","FRIDAY","SATURDAY","SUNDAY" };
  62. static const unsigned int seconds_in_day = 24*60*60;
  63.  
  64. julTy Date::jday(monthTy m, dayTy d, yearTy y)
  65. /*
  66. Convert Gregorian calendar date to the corresponding Julian day number
  67. j.  Algorithm 199 from Communications of the ACM, Volume 6, No. 8,
  68. (Aug. 1963), p. 444.  Gregorian calendar started on Sep. 14, 1752.
  69. This function not valid before that.
  70. */
  71. {
  72.     unsigned long c, ya;
  73.     if (m > 2)
  74.         m -= 3;
  75.     else {
  76.         m += 9;
  77.         y--;
  78.     } /* else */
  79.     c = y / 100;
  80.     ya = y - 100*c;
  81.     return ((146097*c)>>2) + ((1461*ya)>>2) + (153*m + 2)/5 + d + 1721119;
  82. } /* jday */
  83.  
  84. void Date::mdy(monthTy& m, dayTy& d, yearTy& y) const
  85. /*
  86. Convert a Julian day number to its corresponding Gregorian calendar
  87. date.  Algorithm 199 from Communications of the ACM, Volume 6, No. 8,
  88. (Aug. 1963), p. 444.  Gregorian calendar started on Sep. 14, 1752.
  89. This function not valid before that.
  90. */
  91. {
  92.     julTy j = julnum - 1721119;
  93.     y = (yearTy) (((j<<2) - 1) / 146097);
  94.     j = (j<<2) - 1 - 146097*y;
  95.     d = (dayTy)(j>>2);
  96.     j = ((d<<2) + 3) / 1461;
  97.     d = (dayTy)((d<<2) + 3 - 1461*j);
  98.     d = (d + 4)>>2;
  99.     m = (5*d - 3)/153;
  100.     d = 5*d - 3 - 153*m;
  101.     d = (d + 5)/5;
  102.     y = (yearTy)(100*y + j);
  103.     if (m < 10)
  104.         m += 3;
  105.     else {
  106.         m -= 9;
  107.         y++;
  108.     } /* else */
  109. } /* mdy */
  110.  
  111. Date::Date()
  112. /*
  113.     Construct a Date for today's date.
  114. */
  115. {
  116.     long clk = time(0);
  117.     const struct tm* now = localtime(&clk);
  118.     julnum = jday(now->tm_mon+1, now->tm_mday, now->tm_year+1900);
  119. }
  120.  
  121. Date::Date(long dayCount, yearTy referenceYear)
  122. /*
  123.         The base date for this computation is Dec. 31 of the previous year.  That
  124.     is "day zero" in relation to the addition which is to take place.  Therefore,
  125.     we call jday() with the base date and then perform the addition.  Note
  126.     that dayCount may be positive or negative and that this function will
  127.     work the same in either case.
  128. */
  129. {
  130.     julnum = jday(12, 31, referenceYear-1) + dayCount;  
  131. }
  132.  
  133.  
  134. Date::Date(long dayCount)
  135. /*
  136.     Constructs a date with Jan. 1, 1901 as the "day zero".  Date(-1) = Dec. 31, 1900
  137.     and Date(1) = Jan. 2, 1901.
  138. */
  139. {
  140.     julnum = jday(1, 1, 1901) + dayCount;
  141. }
  142.  
  143. bool Date::dayWithinMonth(monthTy month, dayTy day, yearTy year)
  144. {
  145.     if (day <= 0) return NO;
  146.     unsigned daysInMonth  = days_in_month[month-1];
  147.     if (leapYear(year) && month == 2) daysInMonth++;
  148.     if (day > daysInMonth) return NO;
  149.     return YES;
  150. }
  151.  
  152. Date::Date(dayTy day, const char* monthName, yearTy year)
  153. /*
  154.       Construct a Date for the given day, monthName, and year.
  155. */
  156. {
  157.     monthTy m = numberOfMonth(monthName);
  158.     if (year <= 99) year += 1900;
  159.     if (!dayWithinMonth(m, day, year))
  160.         setError(NIHCL_BADMODAY,DEFAULT,this,day,monthName,year);
  161.     julnum = jday(m, day, year);            
  162. }
  163.  
  164. static void skipDelim(istream& strm)
  165. {
  166.     char c;
  167.     if (!strm.good()) return;
  168.     strm >> c;
  169.     while (strm.good() && !isalnum(c)) strm >> c;
  170.     if (strm.good()) strm.putback(c);
  171. }
  172.  
  173. static const char* parseMonth(istream& strm)
  174. /*
  175.     Parse the name of a month from input stream.
  176. */
  177. {
  178.     static char month[10];
  179.     register char* p = month;
  180.     char c;
  181.     skipDelim(strm);
  182.     strm.get(c);
  183.     while (strm.good() && isalpha(c) && (p != &month[10])) {
  184.         *p++ = c;
  185.         strm.get(c);
  186.     }
  187.     if (strm.good()) strm.putback(c);
  188.     *p = '\0';
  189.     return month;
  190. }
  191.  
  192. julTy Date::parseDate(istream& strm)
  193. /*
  194.     Parse a date from the specified input stream.  The date must be in one
  195.     of the following forms: dd-mmm-yy, mm/dd/yy, or mmm dd,yy; e.g.: 10-MAR-86,
  196.     3/10/86, or March 10, 1986.  Any non-alphanumeric character may be used as
  197.     a delimiter.
  198. */
  199. {
  200.     unsigned d,m,y;
  201.     const char* mon;        // name of month 
  202.     if (strm.good()) {
  203.         skipDelim(strm);
  204.         strm >> m;        // try to parse day or month number 
  205.         skipDelim(strm);
  206.         if (strm.eof()) return 0;
  207.         if (strm.fail()) {    // parse <monthName><day><year> 
  208.             strm.clear();
  209.             mon = parseMonth(strm);    // parse month name 
  210.             skipDelim(strm);
  211.             strm >> d;        // parse day 
  212.         }
  213.         else {            // try to parse day number 
  214.             strm >> d;
  215.             if (strm.eof()) return 0;
  216.             if (strm.fail()) {    // parse <day><monthName><year> 
  217.                 d = m;
  218.                 strm.clear();
  219.                 mon = parseMonth(strm);        // parse month name 
  220.             }
  221.             else {            // parsed <monthNumber><day><year> 
  222.                 mon = nameOfMonth(m);
  223.             }
  224.         }
  225.         skipDelim(strm);
  226.         strm >> y;
  227.     }
  228.     if (!strm.good()) return 0;
  229.     return Date(d,mon,y).julnum;
  230. }
  231.  
  232. Date::Date(istream& strm)    { julnum = parseDate(strm); }
  233.  
  234. dayTy Date::dayOfWeek(const char* nameOfDay)
  235. /*
  236.     Returns the number, 1-7, of the day of the week named nameOfDay.
  237. */
  238. {
  239.     {
  240.         String s(nameOfDay);
  241.         register unsigned len = s.size();
  242.         if (len > 2) {
  243.             s.toUpper();
  244.             register const char* p = s;
  245.             for (register unsigned i =0; i<7; i++)
  246.             if (strncmp(p,uc_week_day_names[i],len)==0) return i+1;
  247.         }
  248.     }
  249.     setError(NIHCL_BADDAYNAM,DEFAULT,nameOfDay);
  250.     return 0;    // never executed 
  251. }
  252.  
  253. dayTy Date::daysInYear(yearTy year)
  254. /*
  255.     How many days are in the given yearTy year?
  256. */
  257. {
  258.     if (leapYear(year))
  259.         return 366;
  260.     else
  261.         return 365;
  262. }
  263.  
  264. monthTy Date::numberOfMonth(const char* nameOfMonth)
  265. /*
  266.     Returns the number, 1-12, of the month named nameOfMonth.
  267. */
  268. {
  269.     {
  270.         String s(nameOfMonth);
  271.         register unsigned len = s.size();
  272.         if (len > 2) {
  273.             s.toUpper();
  274.             register const char* p = s;
  275.             for (register unsigned i =0; i<12; i++)
  276.                 if (strncmp(p,uc_month_names[i],len)==0) return i+1;
  277.         }
  278.     }
  279.     setError(NIHCL_BADMONAM,DEFAULT,nameOfMonth);
  280.     return 0;    // never executed 
  281. }
  282.  
  283. bool Date::leapYear(yearTy year)
  284. {
  285. /*
  286.     Algorithm from K & R, "The C Programming Language", 1st ed.
  287. */
  288.     if ((year&3) == 0 && year%100 != 0 || year % 400 == 0)
  289.         return YES;
  290.     else
  291.         return NO;
  292. }
  293.  
  294. bool Date::leap() const
  295. {
  296.     return leapYear(year());
  297. }
  298.  
  299. const char* Date::nameOfMonth(monthTy monthNumber)
  300. /*
  301.     Returns a string name for the month number.
  302. */
  303. {
  304.     if (monthNumber > 12)
  305.         setError(NIHCL_BADMONTH, DEFAULT, monthNumber);
  306.     return month_names[--monthNumber];
  307. }
  308.  
  309. const char* Date::nameOfDay(dayTy weekDayNumber)
  310. /*
  311.     Returns a string name for the weekday number.
  312.     Monday == 1, ... , Sunday == 7
  313. */
  314. {
  315.     if (weekDayNumber > 7)
  316.         setError(NIHCL_BADDAY, DEFAULT, weekDayNumber);
  317.     return week_day_names[--weekDayNumber];
  318. }
  319.  
  320. const char* Date::nameOfMonth() const    { return nameOfMonth(month()); }
  321.  
  322. monthTy Date::month() const
  323. {
  324.     monthTy m; dayTy d; yearTy y;
  325.     mdy(m, d, y);
  326.     return m;
  327. }
  328.  
  329. dayTy Date::firstDayOfMonth(monthTy month) const
  330. {
  331.     if (month > 12)
  332.         setError(NIHCL_BADMONTH, DEFAULT, month);
  333.     unsigned firstDay = first_day_of_month[month-1];
  334.     if (month > 2 && leap()) firstDay++;
  335.     return firstDay;
  336. }
  337.  
  338. Date Date::previous(const char* nameOfDay) const
  339. {
  340.     dayTy this_day_Of_Week, desired_day_Of_Week;
  341.     julTy j;
  342.  
  343. //    Set the desired and current day of week to start at 0 (Monday)
  344. //    and end at 6 (Sunday).
  345.  
  346.     desired_day_Of_Week = dayOfWeek(nameOfDay) - 1; // These functions return a value
  347.     this_day_Of_Week    = weekDay() - 1;        // from 1-7.  Subtract 1 for 0-6.
  348.     j = julnum;
  349.  
  350. //    Have to determine how many days difference from current day back to
  351. //    desired, if any.  Special calculation under the 'if' statement to
  352. //    effect the wraparound counting from Monday (0) back to Sunday (6).
  353.  
  354.     if (desired_day_Of_Week > this_day_Of_Week)
  355.         this_day_Of_Week += 7 - desired_day_Of_Week;
  356.     else
  357.         this_day_Of_Week -= desired_day_Of_Week;
  358.     j -= this_day_Of_Week; // Adjust j to set it at the desired day of week.
  359.     return Date(j);
  360. }
  361.  
  362. dayTy Date::weekDay() const
  363. /*
  364.     Although this seems a little strange, it works.  (julnum + 1) % 7 gives the
  365.     value 0 for Sunday ... 6 for Saturday.  Since we want the list to start at
  366.     Monday, add 6 (mod 7) to this.  Now we have Monday at 0 ... Sunday at
  367.     6.  Simply add 1 to the result to obtain Monday (1) ... Sunday (7).
  368. */
  369. {
  370.     return ((((julnum + 1) % 7) + 6) % 7) + 1;
  371. }
  372.  
  373. yearTy Date::year() const
  374. /*
  375.     Returns the year of this Date.
  376. */
  377. {
  378.     monthTy m; dayTy d; yearTy y;
  379.     mdy(m, d, y);
  380.     return y;
  381. }
  382.  
  383. dayTy Date::day() const
  384. /*
  385.     Returns the day of the year of this Date.  First we need to find what year we
  386.     are talking about, and then subtract this julnum from the julnum for December 31
  387.     of the preceeding year.
  388. */
  389. {
  390.     return julnum - jday(12, 31, year()-1);
  391. }
  392.  
  393. dayTy Date::dayOfMonth() const
  394. {
  395.     monthTy m; dayTy d; yearTy y;
  396.     mdy(m, d, y);
  397.     return d;
  398. }
  399.  
  400. int Date::compare(const Object& ob) const
  401. {
  402.     assertArgSpecies(ob,classDesc,"compare");
  403.     return julnum - castdown(ob).julnum;
  404. }
  405.  
  406. void Date::deepenShallowCopy()    {}
  407.  
  408. unsigned Date::hash() const
  409. {
  410.     return julnum;
  411. }
  412.  
  413. bool Date::isEqual(const Object& ob) const
  414. {
  415.     return ob.isSpecies(classDesc) && *this==castdown(ob);
  416. }
  417.  
  418. const Class* Date::species() const { return &classDesc; }
  419.  
  420. void Date::printOn(ostream& strm) const
  421. {
  422.      strm << setfill(' ') << setw(2) << dayOfMonth() << '-';
  423.      strm.write(nameOfMonth(), 3);
  424.      strm << '-' << setfill('0') << setw(2) << (year() % 100);
  425. }
  426.  
  427. void Date::scanFrom(istream& strm)    { julnum = parseDate(strm); }
  428.  
  429. Date::Date(OIOin& strm)
  430.     : BASE(strm)
  431. {
  432.     strm >> julnum;
  433. }
  434.  
  435. void Date::storer(OIOout& strm) const
  436. {
  437.     BASE::storer(strm);
  438.     strm << julnum;
  439. }
  440.  
  441. Date::Date(OIOifd& fd)
  442.     : BASE(fd)
  443. {
  444.     fd >> julnum;
  445. }
  446.  
  447. void Date::storer(OIOofd& fd) const
  448. {
  449.     BASE::storer(fd);
  450.     fd << julnum;
  451. }
  452.  
  453.  
  454.  
  455.  
  456.